$('.item').each(function(i, el){
  el.style.background = `hsl(${Math.random() * 360}, 60%, 70%)`
})

$.fn.gridup = function({
  width = 200,
  height = 200,
  wgap = 20,
  hgap = 20,
  colnum = 3,
  transition = 500,
  data = []
} = {}){
  let $el = this.eq(0)
  let $bg = $('<div class="m-gridup-bg"></div>')
  let $items = $el.children()
  let len = $items.length
  let $tmp
  let mdpos
  let mdflag = false
  let mz = 10
  let timer = {last: 0, curr: +new Date()}
  let getTopLeft = function({col, row}){
    return {
      left: col * (width + wgap),
      top: row * (height + hgap)
    }
  }
  $el.addClass('m-gridup')
  $el.css({
    width: colnum * (width + wgap) - wgap,
    height: Math.ceil(len / colnum) * (height + hgap) - hgap
  })
  $bg.css({width, height})
  $el.prepend($bg)
  if(data.length >= len){
    $items.each((i, el) => $(el).data('d', data[i]))
  }
  $items.addClass('m-gridup-item')
  $items.css({
    width,
    height,
    transitionDuration: transition + 'ms'
  })
  $items.each(function(i, el){
    let row = Math.floor(i / colnum)
    let col = i % colnum
    let $el = $(el)
    $el.css(getTopLeft({col, row}))
    $el.data('pos', {row, col})
  })
  $items.on('mousedown', function(e){
    $tmp = $(this)
    let tmppos = $tmp.position()
    $bg.css(getTopLeft($tmp.data('pos')))
    mdpos = {
      x: e.pageX - tmppos.left,
      y: e.pageY - tmppos.top
    }
    $el.css('cursor', '-webkit-grab')
    $tmp.css('transition-duration', '0ms')
    $tmp.css('z-index', mz++)
    mdflag = true
  })
  $el.on('mousemove', function(e){
    if(mdflag){
      let offset = {
        top: e.pageY - mdpos.y,
        left: e.pageX - mdpos.x
      }
      $tmp.css(offset)
      timer.curr = +new Date()
      if(timer.curr - timer.last > 100){
        timer.last = timer.curr
        $items.each(function(i, e){
          let $e = $(e)
          let tpos = $tmp.data('pos')
          let epos = $e.data('pos')
          if(tpos.row == epos.row && tpos.col == epos.col) return
          let toff = getTopLeft(tpos)
          let eoff = getTopLeft(epos)
          let ch = height - Math.abs(eoff.top - offset.top)
          let cw = width - Math.abs(eoff.left - offset.left)
          if(ch > 0 && cw > 0 && ch * cw > width * height * 2 / 5){
            $e.css(toff)
            $bg.css(eoff)
            $tmp.data('pos', epos)
            $e.data('pos', tpos)
          }
        })
      }
    }
  })
  $el.on('mouseup', function(){
    $el.css('cursor', '')
    mdflag = false
    let pos = getTopLeft($tmp.data('pos'))
    $tmp.css(pos)
    $tmp.css('transition-duration', transition + 'ms')
  })
  return {
    getSorted(){
      return Array.from($items)
        .map(d => {
          let pos = $(d).data('pos')
          return {
            row: pos.row,
            col: pos.col,
            data: $(d).data('d')
          }
        }).sort((a, b) => a.row == b.row ? a.col - b.col : a.row - b.row)
        .map(d => d.data)
    }
  }
}

let grid = $('.wrapper').gridup({
  data: [1, 2, 3, 4, 5, 6]
})
setInterval(() => {
  console.log(grid.getSorted())
}, 100000)